package cn.edu.seu.ssny.controller.web;

import cn.edu.seu.ssny.common.JsonProcessor;
import cn.edu.seu.ssny.common.JsonResult;
import cn.edu.seu.ssny.common.utils.DateUtils;
import cn.edu.seu.ssny.controller.BaseController;
import cn.edu.seu.ssny.entity.*;
import cn.edu.seu.ssny.service.*;
import cn.edu.seu.ssny.service.specification.SimpleSpecificationBuilder;
import cn.edu.seu.ssny.service.specification.SpecificationOperator;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.persistence.criteria.*;
import javax.servlet.http.HttpSession;
import java.math.BigInteger;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

import static cn.edu.seu.ssny.common.utils.DateUtils.*;

@Controller
@RequestMapping("/web/project/data")
public class CurveController extends BaseController {
    private final static Logger logger = LoggerFactory.getLogger(CurveController.class);


    @Autowired
    private IDeviceService deviceService;

    @Autowired
    private IDeviceDataService deviceDataService;

    @Autowired
    private IDeviceDataStdService deviceDataStdService;

    @Autowired
    private IDeviceCategoryCommonService deviceCategoryCommonService;


    @RequestMapping(value = { "/historycurve"})
    public String history(ModelMap map, HttpSession session) {
        List<DeviceCategoryCommon> categories = deviceCategoryCommonService.findByProject((Project) session.getAttribute("currProject"));
        map.put("categories", categories);
        return "web/data/historycurve";
    }


    @RequestMapping(value = { "/livecurve"}, method = RequestMethod.GET)
    public String live(ModelMap map, HttpSession session) {
        List<DeviceCategoryCommon> categories = deviceCategoryCommonService.findByProject((Project) session.getAttribute("currProject"));
        map.put("categories", categories);
        return "web/data/livecurve";
    }

    @RequestMapping(value = "/add", method = RequestMethod.GET)
    public String add(ModelMap map, HttpSession session) {
        Enterprise enterprise = (Enterprise) session.getAttribute("enterprise");
        Specification<Device> specification = new Specification<Device>() {
            @Override
            public Predicate toPredicate(Root<Device> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicatesList = new ArrayList<>();
                predicatesList.add(cb.equal(root.get("enterprise"), enterprise));
                predicatesList.add(cb.isNull(root.get("categoryCommon")));
                Predicate[] predicates = new Predicate[predicatesList.size()];
                return cb.and(predicatesList.toArray(predicates));
            }
        };
        List<Device> devices = deviceService.findList(specification);
        map.put("devices", devices);
        return "web/data/add";
    }

    @RequestMapping(value= {"/edit"} ,method = RequestMethod.POST)
    @ResponseBody
    public JsonResult edit(ModelMap map, HttpSession session){
        String device_id = request.getParameter("device_id");
        String category_name = request.getParameter("category_name");
        Project project = (Project) session.getAttribute("currProject");
        DeviceCategoryCommon category = deviceCategoryCommonService.findByProjectAndName(project, category_name);
        Device device = null;
        if(!device_id.equals(""))
            device = deviceService.find(Integer.parseInt(device_id));
        try {
            deviceService.setCategory(device, category);
        } catch (Exception e) {
            return JsonResult.failure(e.getMessage());
        }
        return JsonResult.success();
    }

    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String delete(ModelMap map, HttpSession session) {
        return "web/data/delete";
    }

    @RequestMapping(value = "/delelist", method = RequestMethod.POST)
    @ResponseBody
    public Page<DeviceCategoryCommon> deleList(ModelMap map, HttpSession session) {
        Project project = (Project) session.getAttribute("currProject");

        SimpleSpecificationBuilder<DeviceCategoryCommon> builder = new SimpleSpecificationBuilder<DeviceCategoryCommon>();
        builder.add("project", SpecificationOperator.Operator.eq.name(), project);
        Page<DeviceCategoryCommon> page = deviceCategoryCommonService.findAll(builder.generateSpecification(), getPageRequest());
        System.out.println(page.getSize());
        return page;
    }

    @RequestMapping(value = "/delete/{id}", method = RequestMethod.POST)
    @ResponseBody
    public JsonResult delete(@PathVariable Integer id, ModelMap map) {
        try {
            deviceCategoryCommonService.delete(id);
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.failure(e.getMessage());
        }
        return JsonResult.success();
    }


    @RequestMapping(value = "/device-delete/{id}", method = RequestMethod.GET)
    public String deviceDelete(@PathVariable Integer id, ModelMap map, HttpSession session) {
        map.put("categoryId", id);
        return "web/data/device-delete";
    }

    @RequestMapping(value = "/device-delete/list/{id}", method = RequestMethod.GET)
    @ResponseBody
    public Page<Device> deviceDeleteList(@PathVariable Integer id, ModelMap map, HttpSession session) {
        DeviceCategoryCommon category = deviceCategoryCommonService.find(id);
        int deviceSize = category.getDevices().size();
        if(deviceSize == 0)
            return null;
        Integer[] ids = new Integer[deviceSize];
        for(int i = 0; i < deviceSize; i++){
            ids[i] = category.getDevices().get(i).getId();
            System.out.println(ids[i]);
        }
        Specification<Device> specification = new Specification<Device>() {
            @Override
            public Predicate toPredicate(Root<Device> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> predicatesList = new ArrayList<>();
                CriteriaBuilder.In inClause = cb.in(root.get("id"));
                for(int id: ids)
                    inClause.value(id);
                predicatesList.add(cb.and(inClause));
                Predicate[] predicates = new Predicate[predicatesList.size()];
                return cb.and(predicatesList.toArray(predicates));
            }
        };
        Page<Device> page = deviceService.findAll(specification, getPageRequest());
        return page;
    }

    @RequestMapping(value = "/device-delete/{categoryId}/{deviceId}", method = RequestMethod.POST)
    @ResponseBody
    public JsonResult delete(@PathVariable Integer categoryId, @PathVariable Integer deviceId, ModelMap map) {
        try {
            Device device = deviceService.find(deviceId);
            deviceService.deleteCommonCategory(device);
        } catch (Exception e) {
            e.printStackTrace();
            return JsonResult.failure(e.getMessage());
        }
        return JsonResult.success();
    }



    private List<String> getDataList(List<Date> timeList, Device device, boolean isLive,  String mode, int periodMin) {
        List<String> dataList = new ArrayList<>();
        Date start = timeList.get(0);
        Date end = timeList.get(timeList.size()-1);
        Date curr = start;
        while(!isSameTime(curr, end)) {
            Optional<DeviceDataStd> deviceDataStd = deviceDataStdService.findByDeviceAndCreateTime(device, curr);
            if(deviceDataStd.isPresent()) {
                dataList.add(String.valueOf(deviceDataStd.get().getData()));
            }
            else {
                dataList.add("-");
            }
            curr = addTime(curr, isLive, mode, periodMin);
        }
        return dataList;
    }


    private Map<String, Object> packetHistoryData(Map<String,Object> jsonMap) {
        return packetData(jsonMap, false);
    }


    private Map<String, Object> packetData(Map<String,Object> jsonMap, boolean isLive) {
        String startTimeSearch = jsonMap.get("startTimeSearch").toString();
        String endTimeSearch = jsonMap.get("endTimeSearch").toString();
        String mode;
        if(isLive) {
            mode = "minute";
        }
        else {
            mode = jsonMap.get("mode").toString();
        }
        JSONObject json = JSONObject.parseObject(JSON.toJSONString(jsonMap));
        JSONArray jsonDevices = json.getJSONArray("selectedDevices");
        List<Device> deviceList = new ArrayList<>();
        for(Object obj: jsonDevices.toArray()) {
            Device device = deviceService.find(Integer.parseInt(obj.toString()));
            deviceList.add(device);
        }
        if(deviceList.size() == 0)
            return null;
        int periodMin = deviceList.get(0).getPeriod()/60;
        Date start;
        Date end;
        if(StringUtils.isNotBlank(endTimeSearch)) { //重设时间
            end = DateUtils.getRoundTime(DateUtils.parseDate(endTimeSearch), periodMin);
        }
        else {
            end = DateUtils.getRoundTime(new Date(), periodMin);
        }
        if(StringUtils.isNotBlank(startTimeSearch)) {
            start = DateUtils.getRoundTime(DateUtils.parseDate(startTimeSearch), periodMin);
        }
        else {
            start = getDefaultStart(end, mode);
        }

        List<Date> timeList = getTimeList(start, end, isLive, mode, periodMin);
        String tag = null;
        String title = null;
        List<String> legends = new ArrayList<>();
        List<List<String>> yLists = new ArrayList<>();
        for(Object obj: jsonDevices.toArray()){
            Device device = deviceService.find(Integer.parseInt(obj.toString()));
            tag = device.getTemplate().getTag();
            title = device.getCategoryCommon().getName();
            legends.add(device.getName());
            yLists.add(getDataList(timeList, device, isLive, mode, periodMin));
        }

        List<String> xList = new ArrayList<>();
        for (Date date : timeList) {
            xList.add(DateUtils.format(date, getPattern(mode)));
        }

        Map<String, Object> rtn = new HashMap<>();
        rtn.put("xList", xList);
        rtn.put("tag", tag);
        rtn.put("title", title);
        rtn.put("legends", legends);
        rtn.put("yLists", yLists);
        return rtn;
    }


    @RequestMapping(value= {"/chartOne"} ,method = RequestMethod.POST)
    @ResponseBody
    public JsonResult chartOne(@RequestBody Map<String,Object> jsonMap) {
        Map<String,Object> rtn = packetHistoryData(jsonMap);
        if(rtn == null) {
            return JsonResult.failure("请选择设备");
        }
        return JsonResult.success("操作成功", rtn);
    }


    @RequestMapping(value= {"/livecurve"} ,method = RequestMethod.POST)
    @ResponseBody
    public JsonResult liveCurve(@RequestBody Map<String,Object> jsonMap) {
        Map<String,Object> rtn = packetLiveData(jsonMap);
        if(rtn == null) {
            return JsonResult.failure("设备采样数量不一致，无法同时显示，请稍后再试");
        }
        return JsonResult.success("操作成功", rtn);
    }



    private Map<String, Object> packetLiveData(Map<String,Object> jsonMap) {
        String startTimeSearch = jsonMap.get("startTimeSearch").toString();
        String endTimeSearch = jsonMap.get("endTimeSearch").toString();
        JSONObject json = JSONObject.parseObject(JSON.toJSONString(jsonMap));
        JSONArray jsonDevices = json.getJSONArray("selectedDevices");
        List<Device> deviceList = new ArrayList<>();
        for(Object obj: jsonDevices.toArray()) {
            Device device = deviceService.find(Integer.parseInt(obj.toString()));
            deviceList.add(device);
        }
        if(deviceList.size() == 0)
            return null;
        int periodMin = deviceList.get(0).getPeriod()/60;
        Date start;
        Date end;
        if(StringUtils.isNotBlank(endTimeSearch)) { //重设时间
            end = DateUtils.getRoundTime(DateUtils.parseDate(endTimeSearch), periodMin);
        }
        else {
            end = DateUtils.getRoundTime(new Date(), periodMin);
        }
        if(StringUtils.isNotBlank(startTimeSearch)) {
            start = DateUtils.getRoundTime(DateUtils.parseDate(startTimeSearch), periodMin);
        }
        else {
            start = getDefaultStart(end, "hour");
        }

        List<Date> timeList = null;
        String tag = null;
        String title = null;
        List<String> legends = new ArrayList<>();
        List<List<Double>> yLists = new ArrayList<>();
        Integer timeSize = null;
        for(Object obj: jsonDevices.toArray()){
            Device device = deviceService.find(Integer.parseInt(obj.toString()));
            List<DeviceDataStd> deviceDataList = deviceDataStdService.findAllByDeviceAndCreateTimeBetweenOrderByCreateTimeAsc(device, start, end);
            if(timeSize == null){
                timeSize = deviceDataList.size();
                timeList = timeList(deviceDataList);
            }
            else if(!timeSize.equals(deviceDataList.size()))
                return null;

            tag = device.getTemplate().getTag();
            title = device.getCategoryCommon().getName();
            legends.add(device.getName());
            yLists.add(dataList(deviceDataList));
        }
        //除去最后一个时间
        List<String> xList = new ArrayList<>();
        for (Date date : timeList) {
            xList.add(DateUtils.format(date, getPattern("hour")));
        }

        Map<String, Object> rtn = new HashMap<>();
        rtn.put("xList", xList);
        rtn.put("tag", tag);
        rtn.put("title", title);
        rtn.put("legends", legends);
        rtn.put("yLists", yLists);
        return rtn;
    }



    private List<Double> dataList(List<DeviceDataStd> dataList) {
        List<Double> list = new ArrayList<>();

        for(DeviceDataStd data: dataList) {
            list.add(data.getData());
        }
        return list;
    }

    private List<Date> timeList(List<DeviceDataStd> dataList) {
        List<Date> list = new ArrayList<>();

        for(DeviceDataStd data: dataList) {
            list.add(data.getCreateTime());
        }
        return list;
    }

}
